/**
 * Copyright (c) 2021 sdpjw Inc. All rights reserved.
 */
package com.lmn.oval;

import net.sf.oval.ValidationCycle;
import net.sf.oval.configuration.annotation.AbstractAnnotationCheck;
import net.sf.oval.exception.OValException;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * @version 4.0
 * @describe:
 * @author: houkai
 * @Date: 2021/06/03
 */
public class SocialCreditCodeCheckImpl extends AbstractAnnotationCheck<SocialCreditCodeCheck> {

    private static final String BASE_CODE_STRING = "0123456789ABCDEFGHJKLMNPQRTUWXY";
    private static final char[] BASE_CODE_ARRAY = BASE_CODE_STRING.toCharArray();
    private static final List<Character> BASE_CODES = new ArrayList<Character>();
    private static final String BASE_CODE_REGEX = "[" + BASE_CODE_STRING + "]{18}";
    private static final int[] WEIGHT = {1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28};

    static {
        for (char c : BASE_CODE_ARRAY) {
            BASE_CODES.add(c);
        }
    }

    @Override
    public void configure(SocialCreditCodeCheck socialCreditCodeCheck) {
        super.configure(socialCreditCodeCheck);
    }

    /**
     * 判断统一社会信用代码是否有效
     *
     * @param validatedObject 进入校验器的对象
     * @param valueToValidate 注解当前标注的属性值
     * @return 是否通过验证
     * @throws OValException OValException
     */
    @Override
    public boolean isSatisfied(final Object validatedObject, final Object valueToValidate, final ValidationCycle cycle) throws OValException {
        if (validatedObject == null) {
            return true;
        }
        String value = (String) valueToValidate;
        if (!Pattern.matches(BASE_CODE_REGEX, value)) {
            return false;
        }
        char[] businessCodeArray = value.toCharArray();
        char check = businessCodeArray[17];
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            char key = businessCodeArray[i];
            sum += (BASE_CODES.indexOf(key) * WEIGHT[i]);
        }
        int value1 = 31 - sum % 31;
        return check == BASE_CODE_ARRAY[value1 % 31];
    }
}
